﻿using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace ExtenDev.Web.UI
{
    public static class MicroTemplateCompiler
    {
        private static Dictionary<CompilerOptions, MicroTemplateImpl> compilerCache = new Dictionary<CompilerOptions, MicroTemplateImpl>();

        //todo: consider thread syncrhonization
        private static MicroTemplateImpl InternalGetImpl(CompilerOptions opts)
        {
            MicroTemplateImpl ret;
            if (!compilerCache.TryGetValue(opts, out ret)) {
                ret = new MicroTemplateImpl(opts);
                compilerCache.Add(opts, ret);
            }
            return ret;
        }

        public static string GenerateTemplateScript(string template) {
            return GenerateTemplateScript(template, "#", "#", "#=");
        }
        
        public static string GenerateTemplateScript(string template, string blockIdentifier) {

            return GenerateTemplateScript(template, blockIdentifier, blockIdentifier, blockIdentifier + "=");
        }

        public static string GenerateTemplateScript(string template, string startIdentifier, string stopIdentifier, string evalIdentifier) {
            /*

                                         var strFunc = "var p=[],print=function(){p.push.apply(p,arguments);};with(data){p.push('" +
                                                str.replace(/[\r\t\n]/g, " ")
                                               .replace(/'(?=[^#]*#>)/g, "\t")
                                               .split("'").join("\\'")
                                               .split("\t").join("'")
                                               .replace(/<#=(.+?)#>/g, "',$1,'")
                                               .split("<#").join("');")
                                               .split("#>").join("p.push('") +
                                               "');}return p.join('');"; 
  
  
                        */
            CompilerOptions opts = new CompilerOptions() { StartIndentifier = startIdentifier, StopIdentifier = stopIdentifier, EvaluationIdentifier = evalIdentifier };
            MicroTemplateImpl impl = InternalGetImpl(opts);

            return impl.Compile(template);
        }

        private class MicroTemplateImpl {
            private static Regex spaceRemover = new Regex("[\r\t\n]+", RegexOptions.Compiled);
            
            private Regex quotePreserver, inlineEvaluator, blockCompiler;

            public MicroTemplateImpl(CompilerOptions opts) {

                quotePreserver = new Regex("'(?=[^" + opts.StopIdentifier + "]*" + opts.StopIdentifier + ">)", RegexOptions.Compiled);
                inlineEvaluator = new Regex("<" + opts.EvaluationIdentifier + "(.+?)" + opts.StopIdentifier + ">", RegexOptions.Compiled);
                blockCompiler = new Regex("<" + opts.StartIndentifier + "(.+?)" + opts.StopIdentifier + ">", RegexOptions.Compiled);
            }
            
            public string Compile(string template) {

                string ret = spaceRemover.Replace(template, " ");
                ret = quotePreserver.Replace(ret, "\t");
                ret = ret.Replace("'", "\\'").Replace("\t", "'");
                ret = inlineEvaluator.Replace(ret, "',$1,'");
                ret = blockCompiler.Replace(ret, "');$1p.push('");

                ret = "var p=[],print=function(){p.push.apply(p,arguments);};p.push('" +
                    ret + "');return p.join('');";


                return ret;
            }
        }

        private class CompilerOptions {
            public string StartIndentifier, StopIdentifier, EvaluationIdentifier;

            public override bool Equals(object obj)
            {
                if (obj is CompilerOptions) {
                    CompilerOptions o = (CompilerOptions)obj;

                    return o.StartIndentifier.Equals(StartIndentifier) 
                        && o.StopIdentifier.Equals(StopIdentifier) 
                        && o.EvaluationIdentifier.Equals(EvaluationIdentifier);
                }
                return false;
            }

            public override int GetHashCode()
            {
                return (StartIndentifier ?? "").GetHashCode() ^ 
                    (StopIdentifier ?? "").GetHashCode() ^ 
                    (EvaluationIdentifier ?? "").GetHashCode();
            }
            public override string ToString()
            {
                return base.ToString();
            }
        }
    }
}
